﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CamShared;
using AForge.Video.VFW;
using System.Drawing;
using System.Threading;
using System.IO;
using System.Diagnostics;
using System.Text.RegularExpressions;
using DavuxLib;

namespace Recorder
{
    public class Recorder : ICameraOutput
    {

        public Recorder()
        {
            Settings.Get("Recorder|File Length (Minutes)", 120);
            Settings.Get("Recorder|Codec", "wmv3");
            //Settings.SetRestartRequired("Recorder|Codec");
            //Settings.SetRestartRequired("Recorder|File Length (Minutes)");

            try
            {
                if (!Directory.Exists(Settings.Get("Recorder|Video Directory", Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\Recordings\\")))
                {
                    Directory.CreateDirectory(Settings.Get("Recorder|Video Directory", ""));
                }
            }
            catch (
                Exception ex)
            {
                Trace.WriteLine("Error creating recordings directory: " + ex);
            }

            //Settings.SetRestartRequired("Recorder|Video Directory");
        }


        Thread t;
        Dictionary<ICamera, AVIWriter> Writers = new Dictionary<ICamera, AVIWriter>();


        object LCKWriters = new object();

        public void FrameReceived(ICamera camera, System.Drawing.Image frame)
        {

            if (Writers.Keys.Contains(camera))
            {
                lock (LCKWriters)
                {
                    Writers[camera].AddFrame(ResizeBitmap((Bitmap)frame,
                        Settings.Get(camera.Name + "|Recording Width", frame.Width),
                Settings.Get(camera.Name + "|Recording Height", frame.Height)));
                }
            }
        }

        public Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
        {
            Bitmap result = new Bitmap(nWidth, nHeight);
            using (Graphics g = Graphics.FromImage((Image)result))
                g.DrawImage(b, 0, 0, nWidth, nHeight);
            return result;
        }

        public void ErrorOccured(ICamera camera, string Error, bool Fatal)
        {

        }

        public void CameraAttached(ICamera camera)
        {
            if (Settings.Get( camera.Name + "|Recording", false) && !Writers.Keys.Contains(camera))
            {
                Trace.WriteLine("Adding Camera Recorder: " + camera.Name);
                AVIWriter a = null;
                try
                {
                    camera.Activate(); // hold a reference.
                    a = CreateWriter(camera);
                    Writers.Add(camera, a);

                    if (t == null)
                    {
                        t = new Thread(ThreadStart);
                        t.Start();
                    }
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error adding recorder for camera: " + ex);
                }
            }

            Settings.Get(camera.Name + "|Recording Filename ", FSSafe(camera.Name));

            Settings.Get(camera.Name + "|Display Lag Meter", true);
            Settings.Get(camera.Name + "|Show Timestamp", true);

            //Settings.SetRestartRequired(camera.Name + "|Recording");
        }

        private void ThreadStart()
        {

            while (true)
            {
                Thread.Sleep(1000 * 60 * Settings.Get("Recorder|File Length (Minutes)", 120));

                lock (LCKWriters)
                {
                    ICamera[] ca = Writers.Keys.ToArray();
                    AVIWriter[] aw = Writers.Values.ToArray();


                    Writers.Clear();

                    for (int i = aw.Length; i >= 0; i--)
                    {
                        try
                        {
                            ICamera cam = ca[i];
                            AVIWriter a = aw[i];

                            if (a != null)
                            {
                                Trace.WriteLine("Closing Writer (reopen)");
                                a.Close();
                            }

                            a = CreateWriter(cam);
                            Writers.Add(cam, a);
                        }
                        catch (Exception ex)
                        {
                            Trace.WriteLine("Error switching video file: " + ex);
                        }
                    }
                }

            }
        }


        private AVIWriter CreateWriter(ICamera cam)
        {
           // Settings.SetRestartRequired(cam.Name + "|Recording Width");
           // Settings.SetRestartRequired(cam.Name + "|Recording Height");
            AVIWriter a = new AVIWriter(Settings.Get("Recorder|Codec", "wmv3"));
            a.Open(Settings.Get("Recorder|Video Directory", "Recordings\\")
                 + Settings.Get(cam.Name + "|Recording Filename " + cam.Name,
                FSSafe(cam.Name)) + " " + DateTimeFile() + ".avi",
                Settings.Get(cam.Name + "|Recording Width", 320),
                Settings.Get(cam.Name + "|Recording Height", 240));
            a.Quality = Settings.Get(cam.Name + "|Recording Quality (1-100, Default -1)", a.Quality);
            return a;
        }

        private string FSSafe(string p)
        {
            string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars()));
            string invalidReStr = string.Format(@"[{0}]", invalidChars);
            return Regex.Replace(p, invalidReStr, "_");
        }

        private string DateTimeFile()
        {
            return DateTime.Now.Year.ToString() + "-" + DateTime.Now.Month + "-" + DateTime.Now.Day + "__" + DateTime.Now.Hour + "-" + DateTime.Now.Minute;
        }

        public string Name
        {
            get { return "Recorder"; }
        }


        #region IDisposable Members

        public void Dispose()
        {
            foreach (AVIWriter a in Writers.Values)
            {
                Trace.WriteLine("Closing Writer: " + a + a.Position);
                a.Close();
            }
        }

        #endregion
    }
}
